#pragma once

#include "../common/Common.h"
#include "../common/Base.h"
#include "../common/Net.h"

namespace rpc
{

    namespace client
    {

        class Request
        {
        public:
            using ptr = std::shared_ptr<Request>;
            using RequestCallback = std::function<void(const BaseMessage::ptr &)>;
            using AsyncResponse = future<BaseMessage::ptr>;

            struct RequestDescribe
            {
                using ptr = std::shared_ptr<RequestDescribe>;
                BaseMessage::ptr request;
                RType rtype;                        // 返回值类型
                promise<BaseMessage::ptr> response; // 异步回调后结果的存储器
                RequestCallback callback;           // 请求响应时回调
            };

            void onResponse(const BaseConnection::ptr &conn, BaseMessage::ptr &resp)
            {

                DLOG("Request.h onResponse() resv = %s", resp->serialize().c_str());

                string rid = resp->rid();
                RequestDescribe::ptr desc = getDescribe(rid);
                if (!desc)
                {
                    ELOG("收到响应 %s, 但是未找到对应的请求描述！", rid.c_str());
                    return;
                }

                if (desc->rtype == RType::REQ_ASYNC)
                {
                    desc->response.set_value(resp);
                }
                else if (desc->rtype == RType::REQ_CALLBACK && desc->callback)
                {
                    desc->callback(resp);
                }
                else
                {
                    ELOG("请求类型未知");
                }

                delDescribe(rid);
            }

            bool send(const BaseConnection::ptr &conn, const BaseMessage::ptr &req, AsyncResponse &asyncResp)
            {
                RequestDescribe::ptr desc;
                try
                {
                    desc = newDescribe(req, RType::REQ_ASYNC);
                }
                catch (const std::bad_alloc &e)
                {
                    ELOG("请求对象构造失败");
                    return false;
                }

                conn->send(req);
                DLOG("Request.h send() &desc = %p", &desc);
                DLOG("Request.h send() &desc->response = %p", &desc->response);

                asyncResp = desc->response.get_future();
                DLOG("Request.h send() req serialize = %s", req->serialize().c_str());

                return true;
            }

            bool send(const BaseConnection::ptr &conn, const BaseMessage::ptr &req, const RequestCallback &cb)
            {
                RequestDescribe::ptr desc;
                try
                {
                    desc = newDescribe(req, RType::REQ_CALLBACK, cb);
                }
                catch (const std::bad_alloc &e)
                {
                    ELOG("请求对象构造失败");
                    return false;
                }

                conn->send(req);
                return true;
            }

            bool send(const BaseConnection::ptr &conn, const BaseMessage::ptr &req, BaseMessage::ptr &resp)
            {
                AsyncResponse asyncResp;
                if (!send(conn, req, asyncResp))
                {
                    return false;
                }

                DLOG("Request.h send() before get() ...");
                resp = asyncResp.get();
                DLOG("Request.h send() after get() ...");
                return true;
            }

        private:
            RequestDescribe::ptr newDescribe(const BaseMessage::ptr &req, RType type, const RequestCallback &cb = RequestCallback())
            {
                unique_lock<mutex> lock(_mtx);
                RequestDescribe::ptr desc = std::make_shared<RequestDescribe>();
                desc->rtype = type;
                desc->request = req;
                if (desc->rtype == RType::REQ_CALLBACK && cb)
                {
                    desc->callback = cb;
                }

                _requestDesc.emplace(req->rid(), desc);

                return desc;
            }

            RequestDescribe::ptr getDescribe(const string &rid)
            {
                unique_lock<mutex> lock(_mtx);
                auto it = _requestDesc.find(rid);
                if (it == _requestDesc.end())
                {
                    return RequestDescribe::ptr();
                }

                return it->second;
            }

            void delDescribe(const string &rid)
            {
                unique_lock<mutex> lock(_mtx);
                _requestDesc.erase(rid);
            }

        private:
            mutex _mtx;
            unordered_map<string, RequestDescribe::ptr> _requestDesc;
        };

    };

};